#pragma once
#include "View.hpp"
#include"../Comm/httplib.h"
#include"../Comm/protocol.hpp"
#include <mutex>

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_util;

    static const string WebRoot = "./wwwroot";

    //--------------------------
    struct Host
    {
        string _ip;
        uint32_t _port;
        uint16_t _load;
        mutex* _mtx;

        Host(string ip, uint32_t port)
            : _ip(ip), _port(port), _load(0), _mtx(new mutex)
        {}
    };

    class LoadBalance
    {

        const string Conf = "./conf/";
        void LoadHosts_conf()
        {
            string path = Conf + "compiler_hosts.conf";
            ifstream ifs(path);
            if (!ifs.is_open())
            {
                LOG(FATAL, "配置文件 %s 加载错误！", path.c_str());
                exit(-1);
            }

            string line;
            while (getline(ifs, line))
            {
                vector<string> vt = StringUtil::SplitString(line, ":");
                _hosts.emplace_back(vt[0], stoi(vt[1]));
                _online.emplace_back(_hosts.size() - 1);
            }

            if (_hosts.empty())
            {
                LOG(FATAL, "当前配置文件 %s 为空！", path.c_str());
                exit(-1);
            }

            LOG(INFO, "配置文件 %s 加载成功.", path.c_str());
        }

    public:
        LoadBalance()
        {
            LoadHosts_conf();
        }

        // 自主选择主机
        //采用轮询找负载最低的主机
        int SmartChoice(Host** host)
        {
            lock_guard<mutex> lock(_mtx);
            if (_online.empty())
            {
                LOG(ERROR, "所有主机已离线, 请检查并尽快恢复主机服务！");
                return -1;
            }

            for (auto& e : _online)
            {
                _hosts[e]._mtx->lock();
            }
            int id = _online[0];
            for (int i = 1; i < _online.size(); ++i)
            {
                if (_hosts[id]._load > _hosts[_online[i]]._load)
                {
                    id = _online[i];
                }
            }
            *host = &_hosts[id];
            for (auto& e : _online)
            {
                _hosts[e]._mtx->unlock();
            }
            return id;
        }

        //增加负载
        void LoadIncrease_One(int id)
        {
            lock_guard<mutex> lock(*_hosts[id]._mtx);
            _hosts[id]._load += 1;
        }

        //减少负载
        void LoadDescend_One(int id)
        {
            lock_guard<mutex> lock(*_hosts[id]._mtx);
            int load = _hosts[id]._load;
            _hosts[id]._load = load - 1 > 0 ? load - 1 : 0;
        }

       //离线主机
       void OffMachine(int id)
       {
            lock_guard<mutex> lock(_mtx);
            auto it = _online.cbegin();
            while (it != _online.cend())
            {
                if (id == *it)
                {
                    _online.erase(it);
                    _offline.push_back(id);
                    _hosts[id]._load = 0;
                    break;//迭代器可能的失效，因为有break,所以不影响
                }
                ++it;
            }
       } 

       //简单做法 --- 更新_online 为初始值
       void ReOnMachine()
       {
            lock_guard<mutex> lock(_mtx);
            string temp;
            for (auto& e : _offline)
            {
                _online.emplace_back(e);
                temp += to_string(e);
                temp += "  ";
            }
            _offline.clear();

            LOG(INFO, "恢复离线主机: %s", temp.c_str());
       }

    private:
        vector<Host> _hosts; // 提供 Compile_server 的服务主机；来自配置文件 ./conf/hosts.conf

        // 使用 "vector<Machine> _machines" 下标做主机标识
        vector<int> _online;  // 在线主机
        vector<int> _offline; // 离线主机

        mutex _mtx;
    };

    //-=------------------------

    class Control
    {
    public:

        Control(Control** ptr)
        {
            *ptr = this;
        }

        string GetListHtml()
        {
            set<Info> lst = _lib.GetList();
            if (lst.empty())
            {
                return FileUiil::ReadFile(WebRoot + "/404.html");
            }
            return _ve.ListHtml(lst);
        }

        string GetOneQuestionHtml(string id)
        {
            Question qsn;
            bool ret = _lib.GetOneQuestion(id, qsn);
            if (!ret)
            {
                return FileUiil::ReadFile(WebRoot + "/404.html");
            }
            return _ve.QuestionHtml(qsn);
        }

        void ReOnHost()
        {
            _balance.ReOnMachine();
        }

        // 核心功能: 判题
        string Judge(const string& id, const string& submit)
        {
            // 1. 把用户提交的代码和后端测试代码合并, 序列化
            string code = submit + "\n" + _lib.Get_to_be_merged(id);
            unsigned int cpu_limit = _lib.Get_cpu_limit(id);
            unsigned int mem_limit = _lib.Get_mem_limit(id); 

            ns_protocol::Request judge_rst;
            judge_rst.Set(code, cpu_limit, mem_limit);

            string out_json = judge_rst.Serialization();

            // 2. 负载均衡 选择选择 Compile_server 可用主机
            ns_protocol::Response judge_rse;
            while (true)
            {
                Host* ht = nullptr;
                int host_id = _balance.SmartChoice(&ht);
                if (-1 == host_id) 
                {
                    break;
                }

                //选择到一台主机，但是是否可通信暂不可知--->通过主动发起请求检查是否收到响应判断
                _balance.LoadIncrease_One(host_id);
                //客户端 ---- 相对于 Compile_server
                httplib::Client cet(ht->_ip, ht->_port);
                //发起请求
                if (auto res = cet.Post("/compile_and_run", out_json, "application/json"))
                {
                    LOG(INFO, "%d号主机【%s:%d】响应提交服务", host_id, ht->_ip.c_str(), ht->_port);
                    //服务主机在线
                    if (200 == res->status)
                    {
                        //按需返回
                        string in_json = res->body;
                        //反序列化
                        judge_rse.DeSerialization(in_json);

                        _balance.LoadDescend_One(host_id);
                        break;
                    }
                    else
                    {
                        //异常返回
                        _balance.LoadDescend_One(host_id);
                        LOG(ERROR, "服务 Complie_server 响应异常！");
                    }
                }
                else
                {
                    LOG(WARING, "请求%d号主机【%s:%d】无响应...", host_id, ht->_ip.c_str(), ht->_port);
                    _balance.OffMachine(host_id);
                }
                
            }
            return judge_rse.ToString();
        }

    private:
        Model _lib; // 题库数据
        View _ve;   // 网页模块
        LoadBalance _balance;
    };
}